package com.springcloud.interceptor;

import com.springcloud.exception.TokenException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName: TokenInterceptor
 * @author: Linn
 * @Date: 2019年8月19日21:56:41
 */
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Resource
    private RedisTemplate<String,String> redisTemplate;
    /**
     * 不需要校验token的链接
     */
    private static Set<String> set = new HashSet<>();
    static{
        set.add("/error");
    }

    /**
     * 请求处理完成后回调，即呈现后回调
     * view 将调用处理程序执行的任何结果，从而允许进行适当的资源清理
     * 注意:只有当拦截器的preHandle方法已成功完成并返回true!
     * 与postHandle方法一样，将在每个方法上调用该方法拦截器在链中的顺序是相反的，所以第一个拦截器是最后要调用的。
     * 个人解释
     * 重写该方法是为了在回调完成以后执行该方法 验证有没有通过token验证 通过就直接放行
     * 未通过就在控制台打印token解析失败  给自己一个提示...好排查问题
     * @param request
     * @param response
     * @param arg2
     * @param ex
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception ex){
        if (ex != null) {
            log.error("<== afterCompletion - 解析token失败. ex={}", ex.getMessage(), ex);
            this.handleException();
        }
    }

    /**
     *  拦截处理程序的执行。调用HandlerMapping后确定
     *  适当的处理程序对象，但在HandlerAdapter调用处理程序之前。
     *  DispatcherServlet在执行链中处理一个处理程序，包括任意数量的拦截器，处理程序本身位于末尾。
     *  使用这种方法，每个拦截器可以决定中止执行链，通常发送HTTP错误或编写自定义响应。
     *  注意:特别注意异步请求处理。有关详细信息，请参阅org.springframework.web.servlet.AsyncHandlerInterceptor
     *  @param请求当前HTTP请求
     *  @param响应当前HTTP响应
     *  @param处理程序选择要执行的处理程序，用于类型和/或实例评估
     *  如果执行链应该继续执行，则返回true
     *  下一个拦截器或处理程序本身。别的,DispatcherServlet假设这个拦截器已经处理了响应本身。抛出异常，以防出错
     *  重写该方法就是为了判断该请求的rul需不需要被拦截
     *  如果被拦截就说明要验证该请求的token
     * @param request request
     * @param response response
     * @param handler handler
     * @return boolean
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //获得该请求的rui
        String uri = request.getRequestURI();
        if (set.contains(uri)) {
            return true;
        }

        return true;
    }

    /**
     * 拦截处理程序的执行。实际上是在HandlerAdapter之后调用的调用处理程序，但在DispatcherServlet呈现视图之前。
     * 可以通过给定的ModelAndView向视图公开额外的模型对象。
     * DispatcherServlet在执行链中处理一个处理程序，包括任意数量的拦截器，处理程序本身位于末尾。
     * 使用这种方法，每个拦截器都可以对执行进行后处理，按执行链的相反顺序应用。
     * 注意:特别注意异步 org.springframework.web.servlet.AsyncHandlerInterceptor
     * 请求处理。有关详细信息，请参阅
     * @param请求当前HTTP请求
     * @param响应当前HTTP响应
     * @param处理程序处理程序(或{@link HandlerMethod})异步启动执行，用于类型和/或实例检查
     * modelAndView处理程序返回的 modelAndView 也可以是返回null
     * 抛出异常，以防出错
     * 这里只是因为实现了HandlerInterceptor接口 所以必须重写该方法..不做任何修改
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }


    /**
     * 自定义抛出token失效异常
     */
    private void handleException(){
        throw new TokenException();
    }

}
